/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.catalina.ha.authenticator;

import java.util.Map;
import java.io.IOException;

import org.apache.catalina.Session;
import org.apache.catalina.ha.ClusterManager;
import org.apache.catalina.ha.ClusterMessage;
import org.apache.catalina.ha.ClusterListener;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;

/**
 * Receive replicated SingleSignOnMessage form other cluster node.
 * 
 * @author Fabien Carrion
 */
public class ClusterSingleSignOnListener extends ClusterListener {

	private static final Log log = LogFactory
			.getLog(ClusterSingleSignOnListener.class);

	/**
	 * The descriptive information about this implementation.
	 */
	protected static final String info = "org.apache.catalina.session.ClusterSingleSignOnListener/1.0";

	// ------------------------------------------------------------- Properties

	private ClusterSingleSignOn clusterSSO = null;

	// --Constructor---------------------------------------------

	public ClusterSingleSignOnListener() {
	}

	// --Logic---------------------------------------------------

	/**
	 * Return descriptive information about this implementation.
	 */
	public String getInfo() {

		return (info);

	}

	public ClusterSingleSignOn getClusterSSO() {

		return clusterSSO;

	}

	public void setClusterSSO(ClusterSingleSignOn clusterSSO) {

		this.clusterSSO = clusterSSO;

	}

	/**
	 * Callback from the cluster, when a message is received, The cluster will
	 * broadcast it invoking the messageReceived on the receiver.
	 * 
	 * @param myobj
	 *            ClusterMessage - the message received from the cluster
	 */
	public void messageReceived(ClusterMessage myobj) {
		if (myobj != null && myobj instanceof SingleSignOnMessage) {
			SingleSignOnMessage msg = (SingleSignOnMessage) myobj;
			int action = msg.getAction();
			Session session = null;

			if (log.isDebugEnabled())
				log.debug("SingleSignOnMessage Received with action "
						+ msg.getAction());

			switch (action) {
			case SingleSignOnMessage.ADD_SESSION:
				session = getSession(msg.getSessionId(), msg.getContextName());
				if (session != null)
					clusterSSO.associateLocal(msg.getSsoId(), session);
				break;
			case SingleSignOnMessage.DEREGISTER_SESSION:
				session = getSession(msg.getSessionId(), msg.getContextName());
				if (session != null)
					clusterSSO.deregisterLocal(msg.getSsoId(), session);
				break;
			case SingleSignOnMessage.LOGOUT_SESSION:
				clusterSSO.deregisterLocal(msg.getSsoId());
				break;
			case SingleSignOnMessage.REGISTER_SESSION:
				clusterSSO.registerLocal(msg.getSsoId(), null, msg
						.getAuthType(), msg.getUsername(), msg.getPassword());
				break;
			case SingleSignOnMessage.UPDATE_SESSION:
				clusterSSO.updateLocal(msg.getSsoId(), null, msg.getAuthType(),
						msg.getUsername(), msg.getPassword());
				break;
			case SingleSignOnMessage.REMOVE_SESSION:
				session = getSession(msg.getSessionId(), msg.getContextName());
				if (session != null)
					clusterSSO.removeSessionLocal(msg.getSsoId(), session);
				break;
			}
		}
	}

	/**
	 * Accept only SingleSignOnMessage
	 * 
	 * @param msg
	 *            ClusterMessage
	 * @return boolean - returns true to indicate that messageReceived should be
	 *         invoked. If false is returned, the messageReceived method will
	 *         not be invoked.
	 */
	public boolean accept(ClusterMessage msg) {
		return (msg instanceof SingleSignOnMessage);
	}

	private Session getSession(String sessionId, String ctxname) {

		Map managers = clusterSSO.getCluster().getManagers();
		Session session = null;

		if (ctxname == null) {
			java.util.Iterator i = managers.keySet().iterator();
			while (i.hasNext()) {
				String key = (String) i.next();
				ClusterManager mgr = (ClusterManager) managers.get(key);
				if (mgr != null) {
					try {
						session = mgr.findSession(sessionId);
					} catch (IOException io) {
						log.error("Session doesn't exist:" + io);
					}
					return session;
				} else {
					// this happens a lot before the system has started
					// up
					if (log.isDebugEnabled())
						log.debug("Context manager doesn't exist:" + key);
				}
			}
		} else {
			ClusterManager mgr = (ClusterManager) managers.get(ctxname);
			if (mgr != null) {
				try {
					session = mgr.findSession(sessionId);
				} catch (IOException io) {
					log.error("Session doesn't exist:" + io);
				}
				return session;
			} else if (log.isErrorEnabled())
				log.error("Context manager doesn't exist:" + ctxname);
		}

		return null;
	}
}
